home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 February: Technology Seed / Mac Tech Seed Feb '97.toast / OpenDoc 1.2b2c1 / Implementation / Utilities / Crawl.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1997-02-13  |  29.9 KB  |  1,108 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        Crawl.cpp
  3.  
  4.     Contains:    Debugging utility to create and interpret stack crawls
  5.  
  6.     Owned by:    Jens Alfke (based on code from the Macintosh Debugger.)
  7.  
  8.     Copyright:    © 1992 - 1996 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     Change History (most recent first):
  11.  
  12.          <5>     9/17/96    RA        1373060: OD debug stuck in tight loop
  13.                                     looking for *PPC* symbols on *68K*,1373197:
  14.                                     Crawl.cpp doesn't include
  15.                                     <ConditionalMacros.h>,1386098: Crawl can
  16.                                     crash w/Toolbox stuff on non 4-byte aligned
  17.                                     stack
  18.          <4>     6/23/96    TJ        1344349: Need define for MrC.
  19.          <3>     5/24/96    jpa        Fixed comments
  20.          <2>     5/24/96    jpa        1318444: Supports CW 68K and CW9 PPC. Fixed
  21.                                     crashers in New & GetMem.
  22.     In Progress:
  23.         
  24. */
  25.  
  26.  
  27. #ifndef _CRAWL_
  28. #include "Crawl.h"
  29. #endif
  30.  
  31. #ifndef __LOWMEM__
  32. #include <LowMem.h>
  33. #endif
  34.  
  35. #ifndef __MEMORY__
  36. #include <Memory.h>
  37. #endif
  38.  
  39. #ifndef __STRING__
  40. #include <string.h>
  41. #endif
  42.  
  43. #ifndef __STDARG__
  44. #include <stdarg.h>
  45. #endif
  46.  
  47. #ifndef __ERRORS__
  48. #include <Errors.h>
  49. #endif
  50.  
  51. #include <setjmp.h>
  52. #include <stdio.h>
  53. #include <Unmangler.h>
  54.  
  55. #ifndef __CONDITIONALMACROS__
  56. #include <ConditionalMacros.h>
  57. #endif
  58.  
  59. //==============================================================================
  60. // Declarations for embedded-symbol stuff
  61. //==============================================================================
  62.  
  63. typedef unsigned long    ULongWord;
  64. typedef unsigned short    UWord;
  65. typedef unsigned char    UByte;
  66.  
  67. typedef size_t            TargetAddress;
  68.  
  69. typedef OSErr (*ReadMemFn)( void *loc, ULongWord size, void *buffer, va_list args );
  70.  
  71. #define kNoErr            noErr
  72. #define kSymbolNotFound fragSymbolNotFound
  73.  
  74.  
  75. #if GENERATINGPOWERPC
  76. static OSErr LookupPowerPCSym (TargetAddress addr, char *name, TargetAddress *fnBegin, TargetAddress *fnEnd,
  77.         ReadMemFn readMemCallback, ...);
  78. #endif
  79. static OSErr Lookup68KSym (TargetAddress addr, char *name, TargetAddress *fnBegin, TargetAddress *fnEnd,
  80.         ReadMemFn readMemCallback, ...);
  81.  
  82. #define PACKET_MAX_DATA_LENGTH    512
  83.  
  84.  
  85. //==============================================================================
  86. // Stack-crawl types & constants
  87. //==============================================================================
  88.  
  89. struct LinkAreaPPC {
  90.     void*    backChain;
  91.     void*    savedCR;
  92.     void*    savedLR;
  93.     void*    reserved;
  94.     void*    savedTOC;
  95. };
  96.  
  97. struct LinkArea68k {
  98.     void*    backChain;
  99.     void*    returnAddress;
  100. };
  101.  
  102. union LinkArea {
  103.     LinkAreaPPC fPPC;
  104.     LinkArea68k f68k;
  105. };
  106.  
  107. const size_t kMagicA6 = 0xFFFFFFFF;                // Signals 68k->PPC switch
  108. const size_t kPPCInstrLen = 4;                    // Gotta love them RISCs
  109.  
  110.  
  111. //==============================================================================
  112. // Locating Stack Pointer
  113. //==============================================================================
  114.  
  115. #if GENERATING68K
  116.     static void* GetA6( ) = {0x200E}; // MOVE.L A6,D0
  117. #elif GENERATINGPOWERPC
  118.     #ifdef __MWERKS__
  119.         #if __MWERKS__ >= 8
  120.             asm static void* GetSP( ) {
  121.                 mr r3,SP
  122.                 blr
  123.             }
  124.         #else
  125.             const long kjmp_bufStackFrameIndex = 3;            // For CW8 and earlier, not CW9
  126.         #endif
  127.     #elif defined(__MRC__)
  128.         const long kjmp_bufStackFrameIndex = 2;            // For MRC
  129.     #else
  130.         #error "Don't know offset of SP in jmp_buf for this compiler"
  131.     #endif
  132. #else
  133.     #error "What the hell kinda CPU is this?"
  134. #endif
  135.  
  136. //==============================================================================
  137. // StackCrawl
  138. //==============================================================================
  139.  
  140. StackCrawl*
  141. StackCrawl::New( long startAt, long endAt )
  142. {
  143.     StackCrawl *stackCrawl = NULL;
  144.     const void *stackTop;
  145.     
  146.     // Skanky ways to read the CPU registers:
  147. #if GENERATING68K
  148.     stackTop = GetA6();
  149. #elif GENERATINGPOWERPC
  150.     #if defined(__MWERKS__) && __MWERKS__ >= 8
  151.         stackTop = GetSP();
  152.     #else
  153.     {
  154.         jmp_buf regs;
  155.         (void) setjmp(regs);
  156.         stackTop = regs[kjmp_bufStackFrameIndex];
  157.     }
  158.     #endif
  159. #endif
  160.     
  161.     // We're going to do this loop twice. The first time just count the number of frames.
  162.     // Then allocate enough memory to store them. Second time, write the frame data:
  163.     for( ;; ) {
  164.     
  165.         const LinkArea *stackFrame = (LinkArea*)stackTop;
  166.         const LinkArea *lastStackFrame = NULL;
  167.         Boolean isNative = GENERATINGPOWERPC ?true :false;
  168.         const void *pc;
  169.         
  170.         // Crawl up the stack:
  171.         long nFrames = 0;
  172.         while( stackFrame!=NULL && !((long)stackFrame&3) && stackFrame>lastStackFrame ) {
  173.             const LinkArea *nextStackFrame;
  174.             
  175.             if( ! isNative ) {
  176.                 nextStackFrame = (LinkArea*) stackFrame->f68k.backChain;
  177.                 if( ((size_t*)nextStackFrame)[-1] == kMagicA6 ) {        // 68k->PPC switch
  178. #if GENERATINGPOWERPC
  179.                     isNative = true;
  180.                     stackFrame = nextStackFrame;                        // Skip switch frame
  181.                 } else
  182. #else
  183.                     DebugStr("\pOD: PPC frame on 68k machine? Weird!");
  184.                 }
  185. #endif
  186.                     pc = stackFrame->f68k.returnAddress;
  187.             }
  188.  
  189. #if GENERATINGPOWERPC
  190.             if( isNative ) {
  191.                 nextStackFrame = (const LinkArea*) stackFrame->fPPC.backChain;
  192.                 if( (long)nextStackFrame & 1 ) {                    // PPC->68k switch
  193.                     nextStackFrame = (LinkArea*)( (size_t)nextStackFrame -1);
  194.                     isNative = false;
  195.                     pc = nextStackFrame->f68k.returnAddress;
  196.                     nextStackFrame = (const LinkArea*) nextStackFrame->f68k.backChain;
  197.                 } else {
  198.                     pc = (void*)( (size_t)nextStackFrame->fPPC.savedLR - kPPCInstrLen );
  199.                 }
  200.             }
  201. #endif
  202.             
  203.             // Write PC value into list if in write phase:
  204.             if( stackCrawl )
  205.                 if( startAt<=nFrames && nFrames<=endAt )
  206.                     stackCrawl->fFrame[nFrames-startAt] = (const void*)( (size_t)pc | isNative );
  207.             
  208.             // Advance to next frame:
  209.             lastStackFrame = stackFrame;
  210.             stackFrame = (LinkArea*)nextStackFrame;
  211.             nFrames++;
  212.             
  213.             // Stop if we've hit the base of the stack:
  214.             size_t postFrame = (size_t)stackFrame + (isNative ?sizeof(LinkAreaPPC)
  215.                                                               :sizeof(LinkArea68k));
  216.             if( postFrame >= (size_t)LMGetCurStackBase() )
  217.                 break;
  218.         }
  219.         
  220.         if( stackCrawl )
  221.             break;    // Done
  222.         else {
  223.             // We've counted the frames, now allocate storage and go round again:
  224.             // Adjust startAt/endAt:
  225.             if( startAt < 0 )
  226.                 startAt = 0;
  227.             if( endAt <= 0 )
  228.                 endAt += nFrames-1;
  229.             else if( endAt >= nFrames )
  230.                 endAt = nFrames-1;
  231.             if( endAt<startAt )
  232.                 endAt = startAt;
  233.             // Create the StackCrawl object:
  234.             stackCrawl = new (nFrames) StackCrawl(endAt-startAt+1);
  235.         }
  236.     }
  237.     
  238.     return stackCrawl;
  239. }
  240.  
  241.  
  242. void* StackCrawl::operator new( size_t baseSize, long nFrames )
  243. {
  244.     return ::operator new( baseSize + (offsetof(StackCrawl,fFrame)-sizeof(StackCrawl))
  245.                                     + nFrames * sizeof(void*) );
  246. }
  247.  
  248.  
  249. Boolean
  250. StackCrawl::IsFrameNative( long i ) const
  251. {
  252.     return (long)fFrame[i] & 1;
  253. }
  254.  
  255.  
  256. const void*
  257. StackCrawl::GetFramePC( long i ) const
  258. {
  259.     return (const void*)( (long)fFrame[i] & ~1);
  260. }
  261.  
  262.  
  263. static OSErr GetMem( void *loc, ULongWord size, void *buffer, va_list args )
  264. {
  265.     // Try to get away without a CPU exception handler by sanity checking:
  266.     if( loc<&SystemZone()->heapData || (void*)loc>LMGetBufPtr()
  267.                                     || (void*)((size_t)loc+size)>LMGetBufPtr() )
  268.         return -1;
  269.     else {
  270.         BlockMoveData(loc,buffer,size);
  271.         return noErr;
  272.     }
  273. }
  274.  
  275. Boolean
  276. StackCrawl::LookupSymbol( long i, char fnName[], size_t *offset ) const
  277. {
  278.     size_t pc = (size_t) this->GetFramePC(i);
  279.     size_t begin,end;
  280.     
  281.     OSErr err;
  282.     char symbol[256];
  283.     if( this->IsFrameNative(i) )
  284. #if GENERATINGPOWERPC
  285.         err= LookupPowerPCSym(pc,symbol,&begin,&end, &GetMem);
  286. #else
  287.         err= 12345;
  288. #endif
  289.     else
  290.         err= Lookup68KSym(pc,symbol,&begin,&end, &GetMem);
  291.     *offset = pc-begin;
  292.     if( err==noErr && symbol[0] ) {
  293.         // For some reason the traceback names seem to start with "."
  294.         unmangle(fnName,&symbol[1+(symbol[1]=='.')],255);
  295.         return true;
  296.     } else {
  297.         sprintf(fnName,"%08p (%s)", pc, this->IsFrameNative(i) ?"PPC" :"68k");
  298.         return false;
  299.     }
  300. }
  301.  
  302.  
  303. Boolean
  304. StackCrawl::GetFrameName( long i, char name[] ) const
  305. {
  306.     size_t offset;
  307.     
  308.     if( this->LookupSymbol(i,name,&offset) ) {
  309.         sprintf(name+strlen(name)," +%lx", offset);
  310.         return true;
  311.     } else
  312.         return false;
  313. }
  314.  
  315.  
  316. void
  317. StackCrawl::AsString( char str[], long maxLen, const char delimiter[] ) const
  318. {
  319.     char fnName[256];
  320.     size_t offset;
  321.     long len;
  322.     
  323.     str[0] = '\0';
  324.     for( long i=0; i<fNFrames; i++ ) {
  325.         if( i>0 ) {
  326.             len = strlen(delimiter);
  327.             maxLen -= len;
  328.             if( maxLen < 0 )
  329.                 return;
  330.             strcat(str,delimiter);
  331.             str += len;
  332.         }
  333.         this->LookupSymbol(i,fnName,&offset);
  334.         
  335.         len = strlen(fnName);
  336.         maxLen -= len;
  337.         if( maxLen < 0 )
  338.             return;
  339.         strcat(str,fnName);
  340.         str += len;
  341.     }
  342. }
  343.  
  344.  
  345. Boolean
  346. GetNameOfCaller( char str[] )
  347. {
  348.     StackCrawl *c = StackCrawl::New(2,2);
  349.     Boolean gotName = c->GetFrameName(0,str);
  350.     delete c;
  351.     return gotName;
  352. }
  353.  
  354.  
  355. Boolean
  356. StackCrawl::operator== ( const StackCrawl &sc ) const
  357. {
  358.     if( fNFrames != sc.fNFrames )
  359.         return false;
  360.     else
  361.         return memcmp(fFrame,sc.fFrame, fNFrames*sizeof(void*)) ==0;
  362.             
  363. }
  364.  
  365. unsigned long
  366. StackCrawl::Hash ( ) const
  367. {
  368.     unsigned long hash = 0;
  369.     for (long i = 0; i < fNFrames; i++)
  370.     {
  371.         hash ^= (unsigned long) fFrame[i];
  372.     }
  373.     return hash;
  374. }
  375.  
  376.  
  377. //==============================================================================
  378. // Embedded Symbol Extraction (copied from debugger nub)
  379. //==============================================================================
  380.  
  381.  
  382. // max length of an embedded symbol (longer will be truncated)
  383. #define kMaxNameLength                        256
  384. #define kTTblMaxNameLength                    256
  385. #define kMaxTracebackNameSearchLength        512
  386.  
  387.  
  388.  
  389. // PowerPC embedded symbol stuff:
  390.  
  391. #define kMaxPwrPCFnLength    262144
  392. #define kMaxPwrPCFnInstrs    (kMaxPwrPCFnLength >> 2)
  393.  
  394. typedef struct TracebackTblEndAlt
  395.     {
  396.         ULongWord        unknown;
  397.         ULongWord        fnLength;
  398.         UWord            nameLength;
  399.         char            name[kTTblMaxNameLength];
  400.     } TracebackTblEndAlt;
  401.  
  402. typedef struct TracebackTblEnd
  403.     {
  404.         ULongWord        fnLength;
  405.         UWord            nameLength;
  406.         char            name[kTTblMaxNameLength];
  407.     } TracebackTblEnd;
  408.  
  409. typedef struct TracebackTbl
  410.     {
  411.         ULongWord            zero;
  412.         char                version;
  413.         char                language;
  414.         char                flags[6];
  415.         union {
  416.         TracebackTblEnd        end;    // if flags[4] == 0
  417.         TracebackTblEndAlt    endAlt;    // if flags[4] > 0
  418.         } u;
  419.     } TracebackTbl;
  420.  
  421.  
  422. static TracebackTbl *FindTracebackTbl (void *addr, TracebackTbl *ttbl, ReadMemFn readMemCallback, va_list args);
  423.  
  424.  
  425.  
  426. // 68K embedded symbol stuff:
  427.  
  428. #define kMax68KFnLength        32768
  429.  
  430. #define kLINKA6                0x4E56
  431. #define kJMPA0                0x4ED0
  432. #define kRTS                0x4E75
  433. #define kRTD                0x4E74
  434.  
  435.  
  436.  
  437. static UByte    *GetProcStart (UByte *addressOfFnEnd, ReadMemFn readMemCallback, va_list args);
  438. static UByte    *FindNextModule (UByte *start, UByte *limit, Boolean *hasName, ReadMemFn readMemCallback, va_list args);
  439. static Boolean    ValidName (UByte *name, ReadMemFn readMemCallback, va_list args);
  440. static void        GetModuleName (UByte *address, char *name, UByte **dataStart, UByte **dataEnd, ReadMemFn readMemCallback, va_list args);
  441. static Boolean    FindReturn (UByte *start, UByte *limit, UByte **afterReturn, ReadMemFn readMemCallback, va_list args);
  442. static Boolean    LegalSymbolChar (short ch);
  443. static Boolean    LegalTargetChar (short ch);
  444.  
  445. #pragma segment CodeTracking
  446.  
  447. #if GENERATINGPOWERPC
  448. //
  449. // Tries to find a traceback table for the routine in which addr is presumed to be.
  450. // If successful, returns the name of the routine in name and the addresses of the
  451. // beginning and end of the routine in fnBegin and fnEnd.  Else scans forward for
  452. // a blr instruction and backward for an mflr instruction to guesstimate fnBegin
  453. // and fnEnd, and leaves name empty.
  454. //
  455. OSErr LookupPowerPCSym (TargetAddress addr, char *name, TargetAddress *fnBegin, TargetAddress *fnEnd,
  456.         ReadMemFn readMemCallback, ...)
  457. {
  458.     va_list            args;
  459.     TracebackTbl    ttbl;
  460.     TracebackTbl    *where;
  461.     char            *namePtr;
  462.     UWord            nameLength;
  463.     ULongWord        codeStart, fnLength;
  464.     ULongWord        instr;
  465.     long            i, offset;
  466.  
  467.     va_start( args, readMemCallback );
  468.     
  469.     #define    kmflrInstr    0x7C0802A6
  470.     #define    kblrInstr    0x4E800020
  471.     
  472.     *fnBegin = addr;
  473.     *fnEnd = addr;
  474.  
  475.     where = FindTracebackTbl ((void*) addr, &ttbl, readMemCallback, args);
  476.  
  477.     if (where)
  478.     {
  479.         fnLength = ttbl.flags[4] ? ttbl.u.endAlt.fnLength : ttbl.u.end.fnLength;
  480.         codeStart = (ULongWord) where - fnLength;
  481.         offset = addr - codeStart;
  482.         if (offset >= 0)
  483.         {
  484.             namePtr = ttbl.flags[4] ? ttbl.u.endAlt.name : ttbl.u.end.name;
  485.             nameLength = ttbl.flags[4] ? ttbl.u.endAlt.nameLength : ttbl.u.end.nameLength;
  486.             if (nameLength > kMaxNameLength - 2)
  487.                 // leave room for leading length byte and terminating null
  488.                 nameLength = kMaxNameLength - 2;
  489.             strncpy (&name[1], namePtr, nameLength);
  490.             name[0] = nameLength;            // now it's a Pascal string
  491.             name[nameLength + 1] = 0;        // terminating null to make it a TProtocolString
  492.             if (nameLength & 1)                // do we need a pad byte?
  493.                 name[nameLength + 2] = 0;    // add one
  494.             *fnBegin = (TargetAddress) codeStart;
  495.             *fnEnd = (TargetAddress) where;
  496.             return kNoErr;
  497.         }
  498.     }
  499.  
  500.     // couldn't find a traceback, so no embedded name, but we'll scan for the fn boundaries anyway
  501.     for (i = 0; i < kMaxPwrPCFnInstrs; ++i)
  502.     {
  503.         if ((*readMemCallback) ((void*) *fnBegin, 4, &instr, args) != kNoErr)
  504.             return kSymbolNotFound;
  505.         if (instr == kmflrInstr)
  506.             break;
  507.         else if (instr == kblrInstr)
  508.             {
  509.             // probably a leaf routine, and we've backed up into the preceding fn
  510.             *fnBegin += 4;
  511.             break;
  512.             }
  513.         *fnBegin -= 4;
  514.     }
  515.     
  516.     if (i == kMaxPwrPCFnInstrs)
  517.     {
  518.         // didn't find the fn begin
  519.         *fnBegin = addr;
  520.         *fnEnd = addr;
  521.         return kSymbolNotFound;
  522.     }
  523.  
  524.     // found the fn begin, now try to find the fn end
  525.     for (i = 0; i < kMaxPwrPCFnInstrs; ++i)
  526.     {
  527.         if ((*readMemCallback) ((void*) *fnEnd, 4, &instr, args) != kNoErr)
  528.             return kSymbolNotFound;
  529.         *fnEnd += 4;
  530.         if (instr == kblrInstr)
  531.             break;
  532.     }
  533.     
  534.     if (i == kMaxPwrPCFnInstrs)
  535.     {
  536.         // didn't find the fn end
  537.         *fnBegin = addr;
  538.         *fnEnd = addr;
  539.         return kSymbolNotFound;
  540.     }
  541.     
  542.     return kNoErr;
  543. }
  544.  
  545.  
  546. //
  547. // Tries to find a traceback table for the routine in which addr is presumed to be.  If
  548. // successful, returns a pointer to the traceback table and copies its contents into ttbl.
  549. // If not successful, returns NULL.
  550. //
  551. static TracebackTbl *FindTracebackTbl (void *addr, TracebackTbl *ttbl, ReadMemFn readMemCallback, va_list args)
  552. {
  553.     ULongWord        where = (ULongWord) addr;
  554.     ULongWord        i, memLongWord, fnLength;
  555.     char            *name;
  556.     UWord            nameLength;
  557.     
  558.     for (i = 0; i < kMaxPwrPCFnLength; i += 4)
  559.     {
  560.         if ((*readMemCallback) ((void *) where, 4, &memLongWord, args) != kNoErr)
  561.             return NULL;
  562.         if (memLongWord == 0)
  563.             break;
  564.         where += 4;
  565.     }
  566.     
  567.     if (i == kMaxPwrPCFnLength)
  568.         return NULL;
  569.  
  570.     if ((*readMemCallback) ((void *) where, sizeof(TracebackTbl), ttbl, args) != kNoErr)
  571.         return NULL;
  572.     
  573.     // now for some sanity checks
  574.     fnLength = ttbl->flags[4] ? ttbl->u.endAlt.fnLength : ttbl->u.end.fnLength;
  575.     name = ttbl->flags[4] ? ttbl->u.endAlt.name : ttbl->u.end.name;
  576.     nameLength = ttbl->flags[4] ? ttbl->u.endAlt.nameLength : ttbl->u.end.nameLength;
  577.     if ((fnLength > kMaxPwrPCFnLength) || (nameLength > kMaxTracebackNameSearchLength) || (name[0] < ' ') || (name[0] > '}'))
  578.         return NULL;
  579.     else
  580.         return (TracebackTbl *) where;
  581. }
  582. #endif /*GENERATINGPOWERPC*/
  583.  
  584. //
  585. // Tries to find a Macsbug symbol for the routine in which addr is presumed to be.
  586. // If successful, returns the name of the routine in name and the addresses of the
  587. // beginning and end of the routine in fnBegin and fnEnd.
  588. // Name is a "PC" string (a Pascal string with a terminating null).
  589. // fnBegin should normally always be less than or equal to addr, but if addr happens
  590. // to be somewhere strange (like in an embedded symbol itself!), we may actually
  591. // find the next function.
  592. //
  593. OSErr Lookup68KSym (TargetAddress addr, char *name, TargetAddress *fnBegin, TargetAddress *fnEnd,
  594.         ReadMemFn readMemCallback, ...)
  595. {
  596.     va_list            args;
  597.     UByte*            address = (UByte*) addr;
  598.     UByte*            limit = (UByte*) (addr + kMax68KFnLength);
  599.     UByte*            addressOfFnEnd;
  600.     UByte*            codeStart;
  601.     UByte*            ignore1;
  602.     UByte*            ignore2;
  603.     Boolean            hasName;
  604.  
  605.     va_start( args, readMemCallback );
  606.     
  607.     addressOfFnEnd = FindNextModule (address, limit, &hasName, readMemCallback, args);
  608.     
  609.     if (addressOfFnEnd)
  610.         {
  611.         //    10/18/94 dkk
  612.         //    Code here used to search backwards from the end of the function to find the start of this function.
  613.         //    If the start of the function wasn't before the address we were interested in finding the symbol for
  614.         //    then we searched again from the original address. We then would search forward again for the end of
  615.         //    the function. This seemed like a lot of extra work. I changed the code look for the start of the
  616.         //    function from the original start address, and don't look for the end of function twice.
  617.         //    We've found a module for the address, find the start of the procedure.
  618.         //
  619.         codeStart = GetProcStart (address, readMemCallback, args);
  620.  
  621.         if (hasName)
  622.             GetModuleName (addressOfFnEnd, name, &ignore1, &ignore2, readMemCallback, args);
  623.         else
  624.             *name = 0;
  625.             
  626.         *fnBegin = (TargetAddress) codeStart;
  627.         *fnEnd = (TargetAddress) addressOfFnEnd;
  628.         return kNoErr;
  629.         }
  630.     
  631.     *fnBegin = addr;
  632.     *fnEnd = addr + kMax68KFnLength;
  633.     return kSymbolNotFound;
  634. }
  635.  
  636.  
  637. //    
  638. //    GetProcStart
  639. //
  640. //        Search backwards for the start of the procedure and return its address.
  641. //
  642. //        10/18/94 - dkk
  643. //        This code was pulled from MacsBug, which didn't suffer any overhead hit from reading one word of memory
  644. //        at a time. Since when MacsBug is executing, it always has its own bus error handler installed, there
  645. //        is no need to install a special handler for each memory access. For the nubs, this is not the case.
  646. //        Each memory access require installation and removal of the bus error handler. For this reason, this code
  647. //        was changes to read a buffer at a time, and step through the buffer. In addition the check for RTS,
  648. //        JMP (A0), and RTD were moved to here, even though this duplicateds code in FindNextModule. FindNextModule
  649. //        will also do the check, but another memory access is required to do the check. By checking here first,
  650. //        we save the overhead of that memory access most of the time.
  651.  
  652. static UByte *GetProcStart (UByte *addressOfFnEnd, ReadMemFn readMemCallback, va_list args)
  653. {
  654.     UWord        instr;
  655.     UByte        *codeStart;
  656.     UByte        *limit = NULL;
  657.     UByte        *prevDataStart;
  658.     char        prevProcName[kMaxNameLength];
  659.     char        memBuffer[PACKET_MAX_DATA_LENGTH];
  660.     short        offset;
  661.     Boolean        hasName;
  662.  
  663.     if ((ULongWord) addressOfFnEnd >= (ULongWord) kMax68KFnLength)
  664.         limit = addressOfFnEnd - kMax68KFnLength;
  665.  
  666.     codeStart = addressOfFnEnd - 2 - (PACKET_MAX_DATA_LENGTH - 2);
  667.     
  668.     if ((*readMemCallback) ((void *) codeStart, PACKET_MAX_DATA_LENGTH, memBuffer, args) != kNoErr)
  669.         return NULL;
  670.     offset = PACKET_MAX_DATA_LENGTH - 2;
  671.     instr = *(short *) &memBuffer[offset];
  672.  
  673.     //    RTD is a 4 byte instruction. RTS and JMP (A0) are 2 byte instructions.
  674.     //
  675.     if ((instr != kRTS) && (instr != kJMPA0))
  676.         offset -= 2;
  677.  
  678.     //    Step backwards looking for the start of the procedure.
  679.     //
  680.     while (codeStart > limit /* gCurBlock.data */)
  681.         {
  682.         offset -= 2;
  683.  
  684.         if (offset < 0)
  685.         {
  686.             codeStart -= PACKET_MAX_DATA_LENGTH;
  687.             offset = PACKET_MAX_DATA_LENGTH - 2;
  688.             if ((*readMemCallback) ((void *) codeStart, PACKET_MAX_DATA_LENGTH, memBuffer, args) != kNoErr)
  689.                 return NULL;
  690.         }
  691.         instr = *(short *) &memBuffer[offset];
  692.  
  693.         //    LinkA6 starts a procedure.
  694.         //
  695.         if (instr == kLINKA6)
  696.             return (codeStart + offset);
  697.  
  698.         if ((instr == kJMPA0) || (instr == kRTS) || (instr == kRTD))
  699.             {
  700.             addressOfFnEnd = FindNextModule (codeStart + offset, codeStart + offset + 2, &hasName, readMemCallback, args);
  701.             if (addressOfFnEnd)
  702.                 {
  703.                 // Found the previous procedure. Its dataEnd is the same as the codeStart we are looking for.
  704.                 //    10/18/94 dkk
  705.                 //    This used to call GetModuleName all the time. Can save the overhead of determining the module
  706.                 //    name again, by checking hasName returned from FindNextModule.
  707.                 //
  708.                 if (hasName)
  709.                     {
  710.                     GetModuleName (addressOfFnEnd, prevProcName, &prevDataStart, &codeStart, readMemCallback, args);
  711.                     return (codeStart);
  712.                     }
  713.                 else
  714.                     return (addressOfFnEnd);
  715.                 }
  716.             }
  717.         }
  718.     return NULL /* (gCurBlock.data) */;
  719. }
  720.  
  721.  
  722. //
  723. //    FindNextModule
  724. //
  725. //        Search for the next module in the address range start to limit-2.  Module names immediately follow either
  726. //        an RTS, RTD (plus offset), or JMP (A0) instruction.  The three formats for legal module names are described
  727. //        in the header comments for ValidName.  If no module name found, return address of end of module where name
  728. //        would have been.  hasName parameter set to indicate whether a name was found.
  729. //
  730. //        return value    -    address of next module
  731. //
  732.  
  733. static UByte *FindNextModule (UByte *start, UByte *limit, Boolean *hasName, ReadMemFn readMemCallback, va_list args)
  734. {
  735.     UByte *fnEnd;
  736.     UByte *firstFollowingModule = NULL;
  737.  
  738.     while ((start < limit) && FindReturn (start, limit, &fnEnd, readMemCallback, args))
  739.         {
  740.         // After the call to FindReturn, fnEnd contains the address following the return instruction (either an RTS,
  741.         //    RTD, or JMP (A0)).  This will be the address of procedure name, if there is one.
  742.         //
  743.         if (firstFollowingModule == NULL)
  744.             firstFollowingModule = fnEnd;
  745.         
  746.         if (ValidName (fnEnd, readMemCallback, args))
  747.             {
  748.             // Found a module name, return its address.
  749.             //
  750.             *hasName = true;
  751.             return (fnEnd);
  752.             }
  753.         else
  754.             // Wasn't a valid name, keep looking starting after return instruction.
  755.             //
  756.             start = fnEnd;
  757.         }
  758.     
  759.     *hasName = false;
  760.     return firstFollowingModule;
  761. }
  762.  
  763.  
  764. //
  765. //    ValidName
  766. //
  767. //        Checks to see if the name at the pointer one of three valid formats for a module name.
  768. //
  769. //        The three formats are:
  770. //
  771. //            Variable length:    The first byte is in the range $80 to $9F and is a length in the
  772. //                                    range 0 to $1F. The high order bit must be set. A length of 0
  773. //                                    implies the second byte is the actual length in the range $01 
  774. //                                    thru $FF. The length byte(s) and name may be an odd number of
  775. //                                    bytes. However, the data after the name is word aligned.
  776. //            Fixed length 8:        The first byte is in the range $20 to $7F and is an ASCII character.
  777. //                                    The high order bit may be set but is not required to be.
  778. //            Fixed length 16:    The first byte is in the range $20 to $7F and is an ASCII character.
  779. //                                    The high order bit may be set but is not required to be.
  780. //                                    The high order bit in the second byte is required to be set.
  781. //                                    This distinguishes the two types of fixed names.
  782. //
  783.  
  784. static Boolean ValidName (UByte *name, ReadMemFn readMemCallback, va_list args)
  785. {
  786.     char        nameCopy[256];
  787.     UByte        *namePtr = (UByte*) nameCopy;
  788.     UWord        length;
  789.     Boolean        valid;
  790.  
  791.     if ((*readMemCallback) ((void *) name, 256, nameCopy, args) != kNoErr)
  792.         return false;
  793.     
  794.     valid = true;
  795.  
  796.     // The high order bit of the first byte of the name is set for variable length names.
  797.     //
  798.     if (*namePtr & 0x80)
  799.         {
  800.         length = *namePtr++ & 0x7F;
  801.  
  802.         // After clearing the high order bit, the first byte should be in the range of 0-1F, for variable
  803.         //    length names.  If the length is 0, the length is in the next byte.  The next byte should not be 0.
  804.         //
  805.         if (length == 0)
  806.             if (*namePtr)
  807.                 length = *namePtr++;
  808.             else
  809.                 valid = false;
  810.         else
  811.             // Maximum length if in the first byte is 1F.
  812.             //
  813.             if (length > 0x1F)
  814.                 valid = false;
  815.         
  816.         if (valid)
  817.             // So far the name is valid, loop through the name checking for the valid symbol characters 
  818.             //    'a'..'z', 'A'..'Z', '0'..'9', '_', '%', '.' (Variable length names cannot have spaces)
  819.             //    10/18/94 dkk
  820.             //    Added check to see that valid was still true. Used to set valid to whatever the last character
  821.             //    in name used to be.
  822.             //
  823.             for (;(length > 0) && valid; --length)
  824.                 if (valid)
  825.                     valid = LegalSymbolChar ((short) *namePtr++);
  826.         
  827.         }
  828.     else
  829.         {
  830.         //    High order bit in first byte of fixed length name may or may not be set.
  831.         //
  832.         valid = LegalTargetChar ((short) (*namePtr++ & 0x7F));
  833.         if (valid)
  834.  
  835.             {
  836.             // First character was legal, check the high order bit of the second byte to see whether it is a 8 byte or
  837.             // 16 byte fixed length name.
  838.             //
  839.             if (*namePtr & 0x80)
  840.             
  841.                 // 16 byte name.  Check the second character and set the length for the remaining 14 characters.
  842.                 //
  843.                 {
  844.                 valid = LegalTargetChar ((short) (*namePtr++ & 0x7F));
  845.                 length = 14;
  846.                 }
  847.             else
  848.                 // 8 byte name.  Check the second character and set the length for the remaining 6 characters.
  849.                 //
  850.                 {
  851.                 valid = LegalTargetChar ((short) *namePtr++);
  852.                 length = 6;
  853.                 }
  854.             
  855.             //    10/18/94 dkk
  856.             //    Added check to see that valid was still true. Used to set valid to whatever the last character
  857.             //    in name used to be.
  858.             //
  859.             for (;(length > 0) && valid; --length)
  860.                 if (valid)
  861.                     valid = LegalTargetChar ((short) *namePtr++);
  862.             }
  863.         }
  864.     return (valid);
  865. }
  866.     
  867.  
  868. //
  869. //    GetModuleName
  870. //
  871. //   Copy the valid MacsBug module name at address into the name string.
  872. //   Set dataStart to point to the first byte after the name after making sure it is word aligned.
  873. //   Set dataEnd to point to the first byte of the next procedure.
  874. //
  875.  
  876. static void GetModuleName (UByte *address, char *name, UByte **dataStart, UByte **dataEnd, ReadMemFn readMemCallback, va_list args)
  877. {
  878.     UWord        memWord;
  879.     UByte        ch, index;
  880.     char        nameCopy[kMaxNameLength];
  881.     UByte        *namePtr = (UByte*) nameCopy;
  882.  
  883.     *name = 0;
  884.     *dataStart = address;
  885.     *dataEnd = address;
  886.     
  887.     if ((*readMemCallback) ((void *) address, kMaxNameLength, nameCopy, args) != kNoErr)
  888.         return;
  889.  
  890.     // Read first character of name, stripping off most significant bit.
  891.     //
  892.     ch = *namePtr++ & 0x7F;
  893.  
  894.     // Variable length name, if value is 0 thru 1F.
  895.     //
  896.     if (ch < 0x20)
  897.         {
  898.         // The name is the new variable format. This may be a module or a method name.
  899.         //
  900.         if (ch == 0)
  901.             // Second byte is the actual length.
  902.             //
  903.             ch = *namePtr++;
  904.  
  905.         name[0] = 0;
  906.         for (index = 1; index <= ch; ++index)
  907.             // When the name gets over kMaxNameLength, truncate the name and just increment to the end of the name.
  908.             //
  909.             if (index <= kMaxNameLength)
  910.                 name[++name[0]] = *namePtr++;
  911.             else
  912.                 break;
  913.         
  914.         // The name may not be word aligned. Data after the name always starts on a word boundary.
  915.         //    10/18/94 dkk
  916.         //    Added additional 2 character offset to address. In translation from MacsBug my guess is the length
  917.         //    bytes were accidentally omitted. This caused a bug where the last character of the symbol name was
  918.         //    added to the symbol name address as the literal pool size. This caused the returned address range
  919.         //    to not contain the address we were interested in.
  920.         //
  921.         address += ch + 2;
  922.         if ((ULongWord) address % 2)
  923.             ++address;
  924.             
  925.         *dataStart = address;
  926.  
  927.         //    Variable format names are followed by a word which defines the length of the literal pool
  928.         //    after the code of this procedure and before the code of the next procedure. If the word is
  929.         //    odd then assume it is not a length.
  930.         //
  931.         if ((*readMemCallback) ((void *) address, sizeof(UWord), &memWord, args) != kNoErr)
  932.             return;
  933.         if (memWord & 1)
  934.             // Length word is odd, not the length.
  935.             //
  936.             *dataEnd = address;
  937.         else
  938.             // Add length word + length.
  939.             //
  940.             *dataEnd = address + 2 + memWord;
  941.  
  942.         name[name[0] + 1] = 0;            // terminating null to make it a TProtocolString
  943.         if (name[0] & 1)                // do we need a pad byte?
  944.             name[name[0] + 2] = 0;        // add one
  945.         }
  946.     else
  947.         {
  948.         // If the most significant bit of the second character is set, the name is the 16 byte class.method format,
  949.         // other wise its the 8 byte format.
  950.         //
  951.         if (*namePtr & 0x80)
  952.             {
  953.             //    The name is the 16 byte class.method format. Class and method are stored in reverse order in memory.
  954.             // Skip to byte 9 of the name to copy.  Address currently points to byte 2.
  955.             //
  956.             namePtr += 7;
  957.  
  958.             // Initialize length byte.
  959.             //
  960.             name[0] = 0;
  961.             
  962.             // Copy characters in the class.
  963.             //
  964.             for (index = 1; index <= 8; ++index)
  965.  
  966.                 // Strip the spaces used to pad to 8 characters.
  967.                 //
  968.                 if (*namePtr != ' ')
  969.                     name[++name[0]] = *namePtr++;
  970.                 else
  971.                     ++namePtr;
  972.  
  973.             //    Insert the '.' to indicate a method
  974.             //
  975.             name[++name[0]] = '.';
  976.             
  977.             // Reset pointer to beginning of the name.
  978.             //
  979.             namePtr -= 16;
  980.             
  981.             // Copy characters in method
  982.             //
  983.             for (index = 1; index <= 8; ++index)
  984.  
  985.                 // Strip the spaces used to pad to 8 characters.  First two bytes also have most significant bit set.
  986.                 //
  987.                 if (*namePtr & 0x7F != ' ')
  988.                     name[++name[0]] = *namePtr++ & 0x7F;
  989.                 else
  990.                     ++namePtr;
  991.  
  992.             // Skip to byte after end of name.
  993.             //
  994.             address += 16;
  995.             }
  996.         else
  997.             {
  998.             // 8 byte format.  Copy first character and initialize length byte.
  999.             //
  1000.             name[1] = ch;
  1001.             name[0] = 1;
  1002.             
  1003.             // Copy remaining characters in name.
  1004.             //
  1005.             for (index = 2; index <= 8; ++index)
  1006.  
  1007.                 // Strip the spaces used to pad to 8 characters.
  1008.                 //
  1009.                 if (*namePtr != ' ')
  1010.                     name[++name[0]] = *namePtr++;
  1011.                 else
  1012.                     ++namePtr;
  1013.  
  1014.             // Skip to byte after end of name.
  1015.             //
  1016.             address += 8;
  1017.             }
  1018.  
  1019.         //    Fixed length names do not indicate the data length. Assumed immediately after the name.
  1020.         //
  1021.         *dataStart = address;
  1022.         *dataEnd = address;
  1023.  
  1024.         name[name[0] + 1] = 0;            // terminating null to make it a TProtocolString
  1025.         if (name[0] & 1)                // do we need a pad byte?
  1026.             name[name[0] + 2] = 0;        // add one
  1027.         }
  1028.     
  1029. }
  1030.  
  1031.  
  1032. //
  1033. //    FindReturn
  1034. //
  1035. //        Search for an RTS, RTD or JMP (A0) instruction.  The address following the instruction is returned in afterReturn.
  1036. //        Function value is true if return is found, false otherwise.
  1037. //
  1038. //        start                -    Address to start search for return.
  1039. //        limit                -    Address to stop search for return.
  1040. //        afterReturn        -    Address after return instruction (if found).
  1041. //        return value    -    True if return is found, false otherwise.
  1042. //
  1043.  
  1044. static Boolean FindReturn (UByte *start, UByte *limit, UByte **afterReturn, ReadMemFn readMemCallback, va_list args)
  1045. {
  1046.     UWord        memWord;
  1047.     
  1048.     while (start < limit)
  1049.         {
  1050.         if ((*readMemCallback) ((void *) start, sizeof(UWord), &memWord, args) != kNoErr)
  1051.             return false;
  1052.         
  1053.         switch (memWord)
  1054.             {
  1055.             case kJMPA0:
  1056.             case kRTS:
  1057.                 *afterReturn = start + 2;            // JMP (A0) and RTS instruction are both two byte instructions
  1058.                 return (true);
  1059.                 break;
  1060.     
  1061.             case kRTD:
  1062.                 *afterReturn = start + 4;            //    RTD is a four byte instruction.
  1063.                 return (true);
  1064.                 break;
  1065.     
  1066.             default:
  1067.                 start += 2;
  1068.                 break;
  1069.             }
  1070.         }
  1071.         
  1072.     return (false);
  1073. }
  1074.  
  1075.  
  1076. //
  1077. //    LegalSymbolChar
  1078. //
  1079. //        Return True if ch is in the set ['a'..'z', 'A'..'Z', '0'..'9', '_', '%', '.']
  1080. //
  1081.  
  1082. static Boolean LegalSymbolChar (short ch)
  1083. {
  1084.     return (((ch >= 'a') && (ch <= 'z')) ||
  1085.                 ((ch >= 'A') && (ch <= 'Z')) ||
  1086.                  ((ch >= '0') && (ch <= '9')) ||
  1087.                   (ch == '_') || (ch == '%') || (ch == '.'));
  1088. }
  1089.  
  1090.  
  1091. //
  1092. //    LegalTargetChar
  1093. //
  1094. //        Return True if ch is in the set ['a'..'z', 'A'..'Z', '0'..'9', '_', '%', '.', ' '].  Same as LegalSymbolChar
  1095. //        except that a space is also a legal character.
  1096. //
  1097.  
  1098. static Boolean LegalTargetChar (short ch)
  1099. {
  1100.     return (((ch >= 'a') && (ch <= 'z')) ||
  1101.                 ((ch >= 'A') && (ch <= 'Z')) ||
  1102.                  ((ch >= '0') && (ch <= '9')) ||
  1103.                   (ch == '_') || (ch == '%') || (ch == '.') || (ch == ' '));
  1104. }
  1105.  
  1106.  
  1107.  
  1108.